#import "BWCrossStitchLayer.h"
#import "BWGridGeometry.h"
#import "BWCrossStitchSet.h"
#import "BWThread.h"

// temporary for bootstrapping
#import "BWGraphDocument.h"
#import "BWColorKey.h"

#import "BWCrossStitchChangeList.h"

@implementation BWCrossStitchLayer


+ (void) initialize
{
    [super initialize];
    [BWCrossStitchLayer setVersion: 1];

} // initialize


- (BOOL) okToRenderRow: (int) row
                column: (int) column
{
    BWCrossStitch *stitch;

    BOOL ret = YES;

    BOOL weGotOne;
    weGotOne = [changes getSymbol: &stitch
                        atRow: row
                        column: column];
    
    if (!weGotOne) {
        weGotOne = [symbolSet getSymbol: &stitch
                              atRow: row
                              column: column];
    }
    
    if (weGotOne && [self visible]) {

        if ([stitch tag] == kCrossStitchChange_remove) {
            ret = YES;
        } else {
            ret = NO;
        }

    } else {
        ret = [super okToRenderRow: row  column: column];
    }

    return (ret);

} // okToRenderRow

- (id) init
{
    if (self = [super init]) {
    }

    return (self);

} // init


- (void) dealloc
{
    [symbolSet release];
    [changes release];

    [super dealloc];

} // dealloc



- (void) setGeometry: (BWGridGeometry *) geo
{
    [super setGeometry: geo];

    if (symbolSet == nil) {
        symbolSet = [[BWCrossStitchSet alloc] initWithGeometry: geo];
    }

} // setGeometry


- (void) addStitchesForDebugging
{
    BWColorKey *colorKey = [g_globalDocument colorKey];
    NSArray *colorKeyArray = [colorKey threadArrayThingie];

    int count = [colorKeyArray count] - 3;

    int i;
    for (i = 0; i < 25; i++) {
        int row = random() % [geometry countDown];
        int column = random() % [geometry countAcross];

        [symbolSet addStitchWithThread: [colorKeyArray objectAtIndex: i % count]
                   atRow: row
                   column: column];
    }

    for (i = 0; i < 50; i++) {
        int row = random() % [geometry countDown];
        int column = random() % [geometry countAcross];
        BWThread *topThread = nil;
        BWThread *bottomThread = nil;
        BWThread_StitchDivider divider;

        topThread = [colorKeyArray objectAtIndex: random() % count];

        if (random() % 2) {
            bottomThread = [colorKeyArray objectAtIndex: random() % count];
        }
        divider = random() % 3;

        BWCrossStitch *stitch;
        stitch = [[BWCrossStitch alloc]
                     initWithRow: row
                     column: column
                     topThread: topThread
                     bottomThread: bottomThread
                     divider: divider];
        [symbolSet addStitch: stitch];
        [stitch release];
    }

    [self postChangeNotification];
    
} // addStitchesForDebugging



- (void) drawThread: (BWThread *) thread
             inRect: (NSRect) rect
{
    if (thread == nil) {
        return;
    }

    NSColor *threadColor;
    NSColor *darkColor;

    if (![self active]) {
        threadColor = [NSColor lightGrayColor];
        darkColor = threadColor;
    } else {
        threadColor = [[thread color] color];
        darkColor = [NSColor blackColor];
    }

    BOOL drawSymbol = NO;
    BOOL drawFakeStitch = NO;
    NSColor *symbolColor = nil;
    NSColor *fillColor = nil;

    switch ([self displayType]) {
    case kDisplay_ColoredSymbols:
        drawSymbol = YES;
        symbolColor = threadColor;
        break;
    case kDisplay_BlackSymbol:
        drawSymbol = YES;
        symbolColor = darkColor;
        break;
    case kDisplay_Color:
        fillColor = threadColor;
        break;
    case kDisplay_FakeStitch:
        symbolColor = threadColor;
        drawFakeStitch = YES;
        break;
    case kDisplay_Silhouette:
        rect = NSInsetRect (rect, -1, -1);
        fillColor = darkColor;
        break;
    }

    if (fillColor != nil) {
        [fillColor set];
        [NSBezierPath fillRect: rect];
    }

    if (drawSymbol) {
        [symbolColor set];

        // account for grid line
        rect.size.width -= 1.0;
        rect.size.height -= 1.0;

        NSBezierPath *path;
        path = [[thread symbol] pathInRect: rect];
        
        [path fill];

    } else if (drawFakeStitch) {
        rect = NSInsetRect (rect, 2, 2);
        float lineWidth = [NSBezierPath defaultLineWidth];
        NSLineCapStyle capStyle = [NSBezierPath defaultLineCapStyle];

        [NSBezierPath setDefaultLineWidth: 3.0];
        [NSBezierPath setDefaultLineCapStyle: NSRoundLineCapStyle];

        [threadColor set];
        NSPoint start, end;
        
        start = NSMakePoint (NSMinX(rect), NSMinY(rect));
        end = NSMakePoint (NSMaxX(rect), NSMaxY(rect));

        [NSBezierPath strokeLineFromPoint: start  toPoint: end];

        start = NSMakePoint (NSMaxX(rect), NSMinY(rect));
        end = NSMakePoint (NSMinX(rect), NSMaxY(rect));

        [NSBezierPath strokeLineFromPoint: start  toPoint: end];

        [NSBezierPath setDefaultLineWidth: lineWidth];
        [NSBezierPath setDefaultLineCapStyle: capStyle];
    }

} // drawThread


- (void) drawStitch: (BWCrossStitch *) stitch
              atRow: (int) row
             column: (int) column
{
    NSRect rect = [geometry rectForRow: row
                            column: column];

    if ([stitch divider] == kDivider_None) {
        [self drawThread: [stitch topThread]
              inRect: rect];

    } else {
        float x, y, mx, my;
        x = NSMinX (rect);
        y = NSMinY (rect);
        mx = NSMaxX (rect);
        my = NSMaxY (rect);

        float h2, w2;
        h2 = rect.size.height / 2;
        w2 = rect.size.width / 2;

        NSRect topRect, bottomRect;
        NSPoint lineStart, lineEnd;

        if ([stitch divider] == kDivider_Forward) {  // "/"
            topRect = NSMakeRect (x, y, w2, h2); // top left
            bottomRect = NSMakeRect (x + w2, y + h2, w2, h2); // bottom right
            lineStart = NSMakePoint (mx, y);
            lineEnd = NSMakePoint (x, my);

        } else { // "\"
            topRect = NSMakeRect (x + h2, y, w2, h2);
            bottomRect = NSMakeRect (x, y + h2, w2, h2);
            lineStart = NSMakePoint (x, y);
            lineEnd = NSMakePoint (mx, my);
        }
        
        [self drawThread: [stitch topThread]
              inRect: topRect];
        [self drawThread: [stitch bottomThread]
              inRect: bottomRect];

        if ([self displayType] == kDisplay_ColoredSymbols
            || [self displayType] == kDisplay_BlackSymbol) {

            float lineWidth = [NSBezierPath defaultLineWidth];
            [NSBezierPath setDefaultLineWidth: 0.5];

            [[NSColor blackColor] set];
            [NSBezierPath strokeLineFromPoint: lineStart
                          toPoint: lineEnd];
            
            [NSBezierPath setDefaultLineWidth: lineWidth];
        }
    }

} // drawStitch


- (void) maybeDrawStitch: (BWCrossStitch *) stitch
                   atRow: (int) row
                  column: (int) column
{
    if ([nextHigherLayer okToRenderRow: row
                         column: column]) {
        [self drawStitch: stitch
              atRow: row
              column: column];
    }

} // maybeDrawStitch


- (void) drawRect: (NSRect) rect
{
    // draw visible stuff from change layer
    NSEnumerator *enumerator = [changes changeEnumerator];
    BWCrossStitch *change;

    while (change = [enumerator nextObject]) {
        if ([change tag] == kCrossStitchChange_add) {
            
            [self drawStitch: change
                  atRow: [change row]
                  column: [change column]];
        }
    }
    
    int count = [symbolSet count];
    int i;
    for (i = 0; i < count; i++) {
        BWCrossStitch *symbol = [symbolSet symbolAtIndex: i];

        if (changes != nil 
            && [changes isChangeAtRow: [symbol row]
                        column: [symbol column]]) {
            continue;
        }

        NSRect symbolRect;
        symbolRect = [geometry rectForRow: [symbol row]  
                               column: [symbol column]];
        
        if (NSIntersectsRect(rect, symbolRect)) {
            [self maybeDrawStitch: symbol
                  atRow: [symbol row]
                  column: [symbol column]];
        }
    }

} // drawRect



- (BOOL) removeSymbolAtPoint: (NSPoint) mouse
{
    int row, column;
    [geometry getRow: &row
              andColumn: &column
              atPoint: mouse];

    BOOL gotOne = [symbolSet isSymbolAtRow: row  column: column];

    if (gotOne) {
        [symbolSet removeSymbolAtRow: row  column: column];
        [self postChangeNotification];
    }

    return (gotOne);

} // removeSymbolAtPoint


- (BOOL) handleMouseDown: (NSEvent *) event
                 atPoint: (NSPoint) mouse
{
    BOOL gotOne = [self removeSymbolAtPoint: mouse];

    return (gotOne);

} // handleMouseDown


- (void) handleMouseDragged: (NSEvent *) event
                    atPoint: (NSPoint) mouse
{
    (void)[self removeSymbolAtPoint: mouse];
    [self postChangeNotification];

} // handleMouseDragged


- (void) handleMouseUp: (NSEvent *) event
               atPoint: (NSPoint) mouse
{

} // handleMouseUp


- (void) setChangeList: (BWCrossStitchChangeList *) c
{
    changes = [c retain];
    [self postChangeNotification];

} // setChangeList


- (void) applyChanges
{
    [symbolSet applyChangeList: changes];
} // applyChanges


- (BWLayerKind) layerKind
{
    return (kLayerKind_CrossStitch);
} // layerKind

- (BWCrossStitchSet *) crossStitches
{
    return (symbolSet);
} // crossStitches


- (void) setDisplayType: (BWCrossStitchLayer_DisplayType) type
{
    displayType = type;
} // setDisplayType


- (BWCrossStitchLayer_DisplayType) displayType
{
    return (displayType);
} // displayType


- (void) replaceThread: (BWThread *) search
            withThread: (BWThread *) replace
{
    BWCrossStitchChangeList *replacementChangeList;
    replacementChangeList = [[BWCrossStitchChangeList alloc] init];

    int count = [symbolSet count];
    
    int i;
    for (i = 0; i < count; i++) {
        BWCrossStitch *stitch = [symbolSet symbolAtIndex: i];
        BWCrossStitch *replacement;
        replacement = [stitch stitchByReplacingThread: search
                              withThread: replace
                              tag: kCrossStitchChange_add];
        if (replacement != nil) {
            [replacementChangeList addStitch: replacement];
        }
    }
    [self setChangeList: replacementChangeList];
    [self applyChanges];

    [changes release];

} // replaceThread




- (void) encodeWithCoder: (NSCoder *) coder
{
    [super encodeWithCoder: coder];

    [coder encodeObject: symbolSet];
    [coder encodeValueOfObjCType: @encode(BWCrossStitchLayer_DisplayType)
           at: &displayType];

} // encodeWithCoder


- (id) initWithCoder: (NSCoder *) coder
{
    if (self = [super initWithCoder: coder]) {
        symbolSet = [[coder decodeObject] retain];
        [coder decodeValueOfObjCType: @encode(BWCrossStitchLayer_DisplayType)
               at: &displayType];
    }

    return (self);

} // initWithCoder


@end // BWCrossStitchLayer
